package org.opu.queryhandler.n.db;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import org.opu.queryhandler.n.util.Logger;
import org.opu.queryhandler.n.util.StringUtil;

public class DbManager {

    private Connection conn;
    private Statement lastStatement;
    private Logger logger = Logger.getInstance();

    /**
     *  Create new instance with connection to database, according with
     * given user data
     * @param prop connection properties: user data, and database properties
     * @throws ClassNotFoundException if troubles while loading JDBC driver
     * @throws SQLException it database cannot be accessed
     */
    public DbManager(String url, String usr, String pswd, String driver)
            throws ClassNotFoundException, SQLException {
        connect(url, usr, pswd, driver);
    }

    /**
     * The copy constructor
     * @param dbManager parent database manager
     * @see Cloneable
     */
    private DbManager(DbManager dbManager) {
        this.conn = dbManager.conn;
    }

    /**
     *  Create connection to database, according with given URL
     * @param url connection address with additional properties
     * @throws ClassNotFoundException if troubles while loading JDBC driver
     * @throws SQLException it database cannot be accessed
     */
    public final void connect(String url, String usr, String pswd, String driver)
            throws ClassNotFoundException, SQLException {

        try {
            Class.forName(driver);
            if (StringUtil.ifStr(usr) && StringUtil.ifStr(pswd)) {
                conn = DriverManager.getConnection(url, usr, pswd);
            } else {
                conn = DriverManager.getConnection(url);
            }
            logger.info(this, "Open connection: " + conn);
        } catch (ClassNotFoundException ex) {
            logger.error(this, ex);
            throw new ClassNotFoundException("Troubles while loading JDBC "
                    + "driver - " + driver + ": " + url, ex);
        } catch (SQLException ex) {
            logger.error(this, ex);
            throw new SQLException("Database [" + url + "] cannot be accessed "
                    + "by [" + usr + "]", ex);
        }
    }

    public QueryResult sendQuery(String query) throws SQLException {
        clear();
        lastStatement = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
                ResultSet.CONCUR_UPDATABLE);
        ResultSet resultSet = lastStatement.executeQuery(query);
        SQLWarning warnings = lastStatement.getWarnings();
        return new QueryResult(resultSet, warnings, lastStatement);
    }

    public QueryResult getTables() throws SQLException {
        clear();
        DatabaseMetaData data = conn.getMetaData();
        ResultSet result = data.getTables(null, null, "%", new String[]{"TABLE"});
        SQLWarning warnings = result.getWarnings();
        return new QueryResult(result, warnings, null);
    }

    public void clear() throws SQLException{
        if(lastStatement != null){
            lastStatement.close();
        }
    }

    public void commit() throws SQLException{
        conn.commit();
    }

    public void rollback() throws SQLException{
        conn.rollback();
    }

    public boolean isAutoCommit() throws SQLException {
        return conn.getAutoCommit();
    }

    public void setAutoCommit(boolean val) throws SQLException {
        conn.setAutoCommit(val);
    }

    public void closeConnect() throws SQLException {
        logger.info(this, "Close connection: " + conn);
        logger.flush();
        conn.close();
    }

    @Override
    protected void finalize() throws Throwable {
        closeConnect();
    }

    @Override
    protected DbManager clone() throws CloneNotSupportedException {
        return new DbManager(this);
    }
}
